﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Common;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using Fast.Framework.Models;


namespace Fast.Framework.Extensions
{
    /// <summary>
    /// DbDataReader扩展类
    /// </summary>
    public static class DbDataReaderExtensions
    {

        /// <summary>
        /// 数据库列过滤
        /// </summary>
        /// <param name="dataReader">数据读取</param>
        /// <returns></returns>
        private static List<DbColumn> DbColumnFilter(this DbDataReader dataReader)
        {
            return dataReader.GetColumnSchema().Where(w => !string.IsNullOrWhiteSpace(w.ColumnName)).ToList();
        }

        /// <summary>
        /// 第一构建异步
        /// </summary>
        /// <typeparam name="T">返回类型</typeparam>
        /// <param name="dataReader">数据读取</param>
        /// <returns></returns>
        public static async Task<T> FristBuildAsync<T>(this Task<DbDataReader> dataReader)
        {
            var r = await dataReader;
            var type = typeof(T);
            if (type.IsGenericTypeDefinition)
            {
                type = type.MakeGenericType(type.GetGenericTypeDefinition());
            }
            T t = default;
            var dbColumns = r.DbColumnFilter();
            if (dbColumns.Count > 0 && await r.ReadAsync())
            {
                if (type.IsClass == true && !type.Equals(typeof(string)))
                {
                    var propertyInfos = type.GetProperties().Where(p => dbColumns.Exists(c => c.ColumnName == p.Name || c.ColumnName == p.GetCustomAttribute<ColumnAttribute>()?.Name));
                    if (type.IsDefined(typeof(CompilerGeneratedAttribute)))//判断匿名对象
                    {
                        t = (T)Activator.CreateInstance(type, type.GenericTypeArguments.GetTypeDefaultValue().ToArray());
                        foreach (var p in propertyInfos)
                        {
                            if (r[p.Name] is not DBNull)
                            {
                                type.GetField($"<{p.Name}>i__Field",
                                    BindingFlags.NonPublic |
                                    BindingFlags.CreateInstance |
                                    BindingFlags.Instance).SetValue(t, r[p.Name].ChanageType(p.PropertyType));
                            }
                        }
                    }
                    else
                    {
                        var fastSet = propertyInfos.FastSetValueBuild();
                        t = (T)Activator.CreateInstance(type);
                        foreach (var p in propertyInfos)
                        {
                            var columnName = p.Name;
                            if (p.IsDefined(typeof(ColumnAttribute)))
                            {
                                columnName = p.GetCustomAttribute<ColumnAttribute>().Name;
                            }
                            if (r[columnName] is not DBNull)
                            {
                                fastSet[p.Name].Invoke(t, r[columnName].ChanageType(p.PropertyType));
                            }
                        }
                    }
                }
                else
                {
                    t = r[0].ChanageType<T>();
                }
            }
            //如果DataReader没有下一个结果关闭连接
            if (!await r.NextResultAsync())
            {
                await r.CloseAsync();
            };
            return t;
        }

        /// <summary>
        /// 列表构建异步
        /// </summary>
        /// <typeparam name="T">返回类型</typeparam>
        /// <param name="dataReader">数据读取</param>
        /// <returns></returns>
        public static async Task<List<T>> ListBuildAsync<T>(this Task<DbDataReader> dataReader)
        {
            var r = await dataReader;
            var type = typeof(T);
            if (type.IsGenericTypeDefinition)
            {
                type = type.MakeGenericType(type.GetGenericTypeDefinition());
            }
            List<T> data = new List<T>();
            if (type.IsClass && !type.Equals(typeof(string)))
            {
                var dbColumns = r.DbColumnFilter();
                var propertyInfos = type.GetProperties().Where(p => dbColumns.Exists(c => c.ColumnName == p.Name || c.ColumnName == p.GetCustomAttribute<ColumnAttribute>()?.Name));
                if (dbColumns.Count > 0 && propertyInfos.Any())
                {
                    if (type.IsDefined(typeof(CompilerGeneratedAttribute)))//判断匿名对象
                    {
                        while (await r.ReadAsync())
                        {
                            T t = (T)Activator.CreateInstance(type, type.GenericTypeArguments.GetTypeDefaultValue().ToArray());
                            foreach (var p in propertyInfos)
                            {
                                if (r[p.Name] is not DBNull)
                                {
                                    type.GetField($"<{p.Name}>i__Field",
                                        BindingFlags.NonPublic |
                                        BindingFlags.CreateInstance |
                                        BindingFlags.Instance).SetValue(t, r[p.Name].ChanageType(p.PropertyType));
                                }
                            }
                            data.Add(t);
                        }
                    }
                    else
                    {
                        var fastSet = propertyInfos.FastSetValueBuild();
                        while (await r.ReadAsync())
                        {
                            T t = (T)Activator.CreateInstance(type);
                            foreach (var p in propertyInfos)
                            {
                                var columnName = p.Name;
                                if (p.IsDefined(typeof(ColumnAttribute)))
                                {
                                    columnName = p.GetCustomAttribute<ColumnAttribute>().Name;
                                }
                                if (r[columnName] is not DBNull)
                                {
                                    fastSet[p.Name].Invoke(t, r[columnName].ChanageType(p.PropertyType));
                                }
                            }
                            data.Add(t);
                        }
                    }
                }
            }
            else
            {
                while (await r.ReadAsync())
                {
                    if (r[0] is not DBNull)
                    {
                        data.Add(r[0].ChanageType<T>());
                    }
                }
            }
            //如果DataReader没有下一个结果关闭连接
            if (!await r.NextResultAsync())
            {
                await r.CloseAsync();
            };
            return data;
        }

        /// <summary>
        /// 字典构建异步
        /// </summary>
        /// <param name="dataReader">数据读取</param>
        /// <returns></returns>
        public static async Task<Dictionary<string, object>> DictionaryBuildAsync(this Task<DbDataReader> dataReader)
        {
            var r = await dataReader;
            Dictionary<string, object> keyValues = default;
            var dbColumns = r.DbColumnFilter();
            if (dbColumns.Count > 0 && await r.ReadAsync())
            {
                keyValues = new Dictionary<string, object>();
                foreach (var c in dbColumns)
                {
                    keyValues.Add(c.ColumnName, (r[c.ColumnName] is DBNull) ? null : r[c.ColumnName]);
                }
            }
            //如果DataReader没有下一个结果关闭连接
            if (!await r.NextResultAsync())
            {
                r.Close();
            }
            return keyValues;
        }

        /// <summary>
        /// 字典列表构建异步
        /// </summary>
        /// <param name="dataReader">数据读取</param>
        /// <returns></returns>
        public static async Task<List<Dictionary<string, object>>> DictionaryListBuildAsync(this Task<DbDataReader> dataReader)
        {
            var r = await dataReader;
            List<Dictionary<string, object>> data = new List<Dictionary<string, object>>();
            Dictionary<string, object> keyValues;
            var dbColumns = r.DbColumnFilter();
            if (dbColumns.Count > 0)
            {
                while (await r.ReadAsync())
                {
                    keyValues = new Dictionary<string, object>();
                    foreach (var c in dbColumns)
                    {
                        keyValues.Add(c.ColumnName, (r[c.ColumnName] is DBNull) ? null : r[c.ColumnName]);
                    }
                    data.Add(keyValues);
                }
            }
            //如果DataReader没有下一个结果关闭连接
            if (!await r.NextResultAsync())
            {
                await r.CloseAsync();
            }
            return data;
        }

    }
}
